# @Author   : tan90
# @Time     : 2025/1/25 08:58
import json
import math
import random
import signal
import sys
import time
from concurrent.futures.thread import ThreadPoolExecutor
from datetime import datetime

import paho.mqtt.client as mqtt
import yaml
from loguru import logger

# 连接信息
client_id = f"py_rower_{random.randint(1000, 9999)}"
port = 1883
clean_session = True
keepalive = 5

host = "test.ironmanapi.com"
username = "admin"
password = "nttieren0513"

# 模拟用户
users: dict
# 上传频率
upload_frequency: int
# 目标里程
target_mileage: int

stop_flag = False
pool: ThreadPoolExecutor


def mock_instant_data(client, device_code: str, user_key: str, divisor: float):
    time.sleep(1)
    sleep_ms = (1 / upload_frequency) * 1000
    logger.info(f"用户[{user_key}]数据模拟任务开始执行...")

    distance = 0
    frequency = 0
    speed = 0
    power = 0
    hr = 0
    calorie = 0
    time_per500m = 0
    time_ms = 0
    topic = f"thing/up/property/{product_code}/{device_code}"

    start_timestamp = datetime.now().timestamp() * 1000
    while not stop_flag:
        time.sleep(sleep_ms / 1000)
        instant_data = {
            "data": {
                "dataType": "instant",
                "distance": distance,
                "frequency": frequency,
                "speed": speed,
                "power": power,
                "hr": hr,
                "calorie": round(calorie, 1),
                "timePer500m": time_per500m,
                "sportMode": "1",
                "time": int(time_ms)
            },
            "ts": f"{datetime.now().strftime("%Y%m%d%H%M%S%f")[:17]}",
            "deviceCode": device_code,
            "user": {
                "userKey": user_key,
            }
        }
        client.publish(topic, payload=json.dumps(instant_data), qos=1)
        if distance >= target_mileage:
            logger.info(f"用户[{user_key}]滑行里程已达到目标值: {target_mileage}m,实际里程: {distance}m")
            break
        distance += int(random.randint(2, 3) * divisor)
        frequency = random.randint(38, 45)
        speed = round(random.uniform(8, 14), 1)
        power = random.randint(300, 500)
        hr = random.randint(110, 160)
        calorie += random.uniform(0.5, 1.0) * divisor
        time_per500m = random.randint(200, 300)
        time_ms = datetime.now().timestamp() * 1000 - start_timestamp
        logger.info(f"用户[{user_key}]里程: {instant_data['data']['distance']}m")


def initMqtt():
    def on_connect(client, userdata, flags, rc):
        if rc == 0:
            # 订阅主题
            logger.info(f"【{host} {port}】mqtt客户端连接成功")
            for user in users:
                user_key = user["user_key"]
                pool.submit(mock_instant_data, mqtt_client, user["device_code"], user_key,
                            user["divisor"])
                logger.info(f"用户[{user_key}]数据模拟任务已任务提交，待启动...")

    def on_disconnect(client, userdata, rc):
        logger.info(f"【{host} {port}】mqtt客户端断开连接 {rc}")

    mqtt_client = mqtt.Client(client_id=client_id, clean_session=clean_session)
    mqtt_client.username_pw_set(username, password)
    mqtt_client.on_connect = on_connect
    mqtt_client.on_disconnect = on_disconnect
    mqtt_client.connect(host, port, keepalive)

    def signal_handler(signum, frame):
        logger.info(f"正在关闭 MQTT 客户端...")
        global stop_flag
        stop_flag = True
        logger.info("正在关闭线程池...")
        pool.shutdown()
        mqtt_client.loop_stop()
        mqtt_client.disconnect()
        sys.exit(0)

    signal.signal(signal.SIGINT, signal_handler)

    # 保持网络循环，等待消息
    mqtt_client.loop_forever()


def initConfig(config_path: str):
    with open(config_path, 'r', encoding="utf-8") as f:
        return yaml.safe_load(f)


def initLog(level: str):
    logger.remove()
    format = "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | " \
             "<level>{level}</level> | " \
             "<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> | " \
             "<level>{message}</level>"
    logger.add(sys.stderr, format=format, level=level)


if __name__ == "__main__":
    # 初始化配置
    config = initConfig('config.yaml')
    initLog(config['level'])

    users = config['users']
    upload_frequency = config['upload_frequency']
    target_mileage = config['target_mileage']
    product_code = config['product_code']
    pool = ThreadPoolExecutor(max_workers=len(users))

    logger.info(f"皮划艇[{product_code}]数据模拟程序启动...")
    logger.info(f"上传频率为{upload_frequency}Hz，模拟用户数为{len(users)}个")

    initMqtt()
